# Prefix Values
EXA = 10^18
PETA = 10^15
TERA = 10^12
GIGA = 10^9
MEGA = 10^6
KILO = 10^3

DEF_transformValue <- function (x) sprintf ("%f", x)
DEF_transformValue_int <- function (x) sprintf ("%d", round (x))

genSIPrefix <- function (x, xform=DEF_transformValue, sep=" ") {
  if (x >= EXA) { p <- "E" ; s <- EXA }
  else if (x >= PETA) { p <- "P" ; s <- PETA }
  else if (x >= TERA) { p <- "T" ; s <- TERA }
  else if (x >= GIGA) { p <- "G" ; s <- GIGA }
  else if (x >= MEGA) { p <- "M" ; s <- MEGA }
  else if (x >= KILO) { p <- "k" ; s <- KILO }
  else { p <- "" ; s <- 1 }
  sprintf ("%s%s%s", xform (x/s), sep, p)
}

genSIPrefix_int <- function (x, xform=DEF_transformValue_int) {
  genSIPrefix (x, xform=DEF_transformValue_int, sep='')
}

genSIPrefixValue <- function (x, sigfigs) {
  genSIPrefix (x, function (y) genSigFigLabels (y, sigfigs))
}

# Word size [Bytes]
if (USE_DOUBLE_PREC) {
  WORD <- 8 # double-precision
  FLOP_FAC <- 1
} else {
  WORD <- 4 # single-precision
  FLOP_FAC <- 2
}
CPLEX_WORD <- 2 * WORD  # complex-word size (real, imaginary) [Bytes]

# Bytes -> Words
bytesToWords <- function (bytes) { return(bytes / WORD) }

#
# Exponential growth function: Projects the value in 't' years,
# assuming the value starts at 't0' and doubles every 'ttd' years
#
#   t0 : Value at time t=0
#   ttd : Doubling time
#   t : Time at which to evaluate the function
#
growthProj <- function (t0, ttd, t) { return(t0 * 2^(t/ttd)) }

#==========================================================
# Defines processor and system classes
#==========================================================

setClass ("Processor", representation (desc="character" # Descriptive name
                                       , C0="function" # clock rate [cy/s]
                                       , p0="function" # no. cores on this processor
                                       , beta_mem="function" # memory bandwidth [B/s]
                                       , Z="function" # fast memory size [B]
                                       , L="function" # memory transfer size [B]
                                       ))

newProc <- function (desc, C0, p0, beta_mem, Z, L)
  new ("Processor", desc=desc, C0=C0, p0=p0, beta_mem=beta_mem, Z=Z, L=L)

setClass ("System",
          representation (desc="character"  # Descriptive name
                          , Proc="Processor"  # Base processor
                          , Sockets="function"  # Total no. of sockets
                          , beta_link="function"  # link bandwidth
                          ))

# Create a new cluster system
newSys <- function (Proc, Sockets, beta_link)
  new ("System", desc="", Proc=Proc, Sockets=Sockets, beta_link=beta_link)

#==========================================================
# Some helper functions
#==========================================================

getProc <- function (S) S@Proc

getSocketPeak <- function (S) getProc (S)@C0
getSocketCores <- function (S) getProc (S)@p0
getSocketCorePeak <- function (S) function (t) getSocketPeak (S) (t) / getSocketCores (S) (t)
getSocketBW <- function (S) getProc (S)@beta_mem
getSocketZ <- function (S) getProc (S)@Z
getSocketL <- function (S) getProc (S)@L

getSockets <- function (S) S@Sockets
getPeak <- function (S) function (t) S@Sockets (t) * (getSocketPeak (S) (t))
getCores <- function (S) function (t) S@Sockets (t) * (getSocketCores (S) (t))
getLinkBW <- function (S) S@beta_link

getAggMemBW <- function (S) function (t) (getSockets (S) (t)) * (getSocketBW (S) (t))
getBisectionBW <- function (S) function (t) {
  n_sockets <- getSockets (S) (t)
  beta_link <- getLinkBW (S) (t)
  if (USE_NET_TORUS3D) {
    bisectionWidth <- n_sockets^(2 / 3)
  } else {
    bisectionWidth <- 0.5 * n_sockets
  }
  bisectionBandwidth <- bisectionWidth * beta_link
  return (bisectionBandwidth)
}
getAggLinkBW <- function (S) function (t) (getSockets (S) (t)) * (get (S) (t))

getDesc <- function (S) { if (S@desc == "") getProc (S)@desc else S@desc }

#==========================================================
# Define baseline processors for the swim-lane systems
#==========================================================

# Base processor for swim lane 1
CPU_base <- newProc (desc="CPU"
                     , C0=function (t) { growthProj (134.4*(FLOP_FAC/2)*GIGA, 1.7, t) }
                     , p0=function (t) { growthProj (6, 1.87, t) }
                     , beta_mem=function (t) { growthProj (bytesToWords (26.5*GIGA), 2.9, t) }
                     , Z=function (t) { growthProj (bytesToWords (12.0*MEGA), 2.0, t) }
                     , L=function(t) { bytesToWords (768.0) })

# Base processor for swim lane 2
GPU_base <- newProc (desc="GPU"
                     , C0=function (t) { growthProj (1030.0*(FLOP_FAC/2)*GIGA, 1.7, t) }
                     , p0=function (t) { growthProj (448, 1.87, t) }
                     , beta_mem=function (t) { growthProj (bytesToWords (144.0*GIGA), 3.0, t) }
                     , Z=function (t) { growthProj (bytesToWords (2.7*MEGA), 2.0, t) }
                     , L=function (t) { bytesToWords (128.0) })

#==========================================================
# Create a new cluster system with a specified peak performance
#==========================================================

DEF_beta_link <- function (t) { growthProj (bytesToWords (10.0*GIGA), 2.25, t) }

newSysPeak <- function (Proc, peak, beta_link=DEF_beta_link)
  newSys (Proc=Proc
          , Sockets=function (t) { ceiling (peak / Proc@C0 (t)) }
          , beta_link=beta_link)

#==========================================================
# eof
#==========================================================
